﻿using System.Collections.Generic;
using System.ComponentModel.Composition;

namespace MefContrib.Models.Provider.Tests
{
    using System;
    using NUnit.Framework;

    [TestFixture]
    public class ComposablePropertyTests
    {
        [Test]
        public void NonStaticPropertyShouldRequireAnInstance()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadWrite")[0]);

            Assert.IsTrue(composable.IsInstanceNeeded);
        }

        [Test]
        public void StaticPropertyShouldNotRequireAnInstance()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("StaticReadWrite")[0]);

            Assert.IsFalse(composable.IsInstanceNeeded);
        }

        [Test]
        public void ReadonlyPropertyShouldNotBeWritable()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadonly")[0]);

            Assert.IsFalse(composable.IsWritable);
        }

        [Test]
        public void WriteonlyPropertyShouldNotBeReadable()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticWriteonly")[0]);

            Assert.IsFalse(composable.IsReadable);
        }

        [Test]
        public void ReturnTypeShouldReturnTypeOfProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadWrite")[0]);

            Assert.IsTrue(composable.ReturnType == typeof(int));
        }

        [Test]
        public void DeclaringTypeShouldReturnTypeOfClass()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadWrite")[0]);

            Assert.IsTrue(composable.DeclaringType == typeof(FakePropertyPart));
        }

        [Test]
        public void PropertyShouldNotBeNull()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadWrite")[0]);

            Assert.IsNotNull(composable.Property);
        }

        [Test, ExpectedException(typeof(InvalidOperationException))]
        public void SetValueShouldThrowExceptionOnReadonlyProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadonly")[0]);

            FakePropertyPart part =
                new FakePropertyPart();

            composable.SetValue(part, 0);
        }

        [Test]
        public void SetValueShouldNotFailOnWritableProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadWrite")[0]);

            FakePropertyPart part =
                new FakePropertyPart();

            composable.SetValue(part, 10);

            Assert.IsTrue(true);
        }

        [Test]
        public void SetValueShouldAssignTheCorrectValueToProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadWrite")[0]);

            FakePropertyPart part =
                new FakePropertyPart();

            composable.SetValue(part, 10);

            int returnValue =
                (int)composable.GetValue(part);

            Assert.AreEqual(returnValue, 10);
        }

        [Test, ExpectedException(typeof(InvalidOperationException))]
        public void GetValueShouldThrowExceptionOnWriteonlyProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticWriteonly")[0]);

            FakePropertyPart part =
                new FakePropertyPart();

            composable.GetValue(part);
        }
        
        [Test]
        public void GetValueShouldNotFailOnReadableProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadWrite")[0]);

            FakePropertyPart part =
                new FakePropertyPart();

            composable.GetValue(part);

            Assert.IsTrue(true);
        }

        [Test]
        public void GetValueShouldReturnCorrectValue()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadonly")[0]);

            FakePropertyPart part =
                new FakePropertyPart();

            int returnValue =
                (int)composable.GetValue(part);

            Assert.AreEqual(returnValue, 10);
        }

        [Test]
        public void IsLazyTypeShouldBeFalseForNonLazyProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonStaticReadWrite")[0]);

            Assert.IsFalse(composable.IsLazyType);
        }

        [Test]
        public void IsLazyTypeShouldBeTrueForLazyProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("LazyType")[0]);

            Assert.IsTrue(composable.IsLazyType);
        }

        [Test]
        public void IsLazyTypeShouldBeFalseForNonLazyCollectionProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("NonLazyCollection")[0]);

            Assert.IsFalse(composable.IsLazyType);
        }

        [Test]
        public void IsLazyTypeShouldBeTrueForLazyCollectionProperty()
        {
            ComposableProperty composable =
                new ComposableProperty(typeof(FakePropertyPart).GetMember("LazyCollection")[0]);

            Assert.IsTrue(composable.IsLazyType);
        }
    }

    public class FakePropertyPart
    {
        public int NonStaticReadWrite { get; set; }
        public static int StaticReadWrite { get; set; }

        public int NonStaticReadonly
        {
            get { return 10; }
        }

        public int NonStaticWriteonly
        {
            set { }
        }

        public Lazy<string> LazyType { get; set; }

        public IEnumerable<Lazy<string>> LazyCollection { get; set; }

        public List<string> NonLazyCollection { get; set; }
    }
}
